/*
 * File         : semaphore.c
 * This file is part of POSIX-RTOS
 * COPYRIGHT (C) 2015 - 2016, DongHeng
 * 
 * Change Logs:
 * DATA             Author          Note
 * 2015-12-11       DongHeng        create
 */

#include "semaphore.h"
#include "pthread.h"
#include "thread_info.h"
#include "stdlib.h"
#include "debug.h"
#include "platform.h"

#ifndef SEM_VALUE_MAX
    #define SEM_VALUE_MAX 32
#endif
 
/*
 * sem_init - the function will initialize the semaphore
 *
 * @param sem     - the point of the semaphore
 * @param pshared - enable if the semaphore can be shared between processes
 * @param value   - the initialized value of the semaphore
 *
 * @return
 *           0 : success
 *     -EINVAL : the value argument exceeds SEM_VALUE_MAX
 *     -ENOSPC : a resource required to initialize the semaphore has been exhausted,
 *               or the limit on semaphores SEM_NSEMS_MAX has been reached
 *      -EPERM : the process lacks appropriate privileges to initialize the semaphore
 */
int sem_init(sem_t *sem, int pshared, unsigned value)
{
    if (value > SEM_VALUE_MAX)
        return -EINVAL;
  
    sem->max_count = SEM_VALUE_MAX;
    sem->count = value;
    list_init(&sem->wait_list);
    pthread_spin_init(&sem->wait_lock, 0);

    return 0;
}

/*
 * sem_post - the function will post the semaphore
 *
 * @param sem - the point of the semaphore
 *
 * @return
 *           0 : success
 *     -EINVAL : the sem argument does not refer to a valid semaphore
 */
int sem_post(sem_t *sem)
{
	struct thread_info *thread_info;

#if PTHREAD_DEBUG_LEVEL      
	if (!sem)
		return -EINVAL;
#endif
    
    pthread_spin_lock(&sem->wait_lock);
    
    if (sem->count < SEM_VALUE_MAX)
        sem->count++;
    
	if (!list_is_empty(&sem->wait_list)) {
        thread_info = LIST_HEAD_ENTRY(&sem->wait_list, struct thread_info, list);
        list_remove_node(&thread_info->list);
        raw_resume_task(thread_info->task);
	}

    pthread_spin_unlock(&sem->wait_lock);
    
	return 0;
}

/*
 * sem_post - the function will wait until the semaphore is not empty
 *
 * @param sem - the point of the semaphore
 *
 * @return
 *           0 : success
 *    -EDEADLK : a deadlock condition was detected
 *      -EINTR : a signal interrupted this function
 *     -EINVAL : the sem argument does not refer to a valid semaphore
 */
int sem_wait(sem_t *sem)
{
	struct thread_info *task;
    
#if SEM_DEBUG_LEVEL      
	if (!sem)
		return -EINVAL;
#endif
    
    task = get_current();
	if (!task)
		return -EDEADLK;
    
    pthread_spin_lock(&sem->wait_lock);
    
    if (sem->count) {
        sem->count--;
    } else {
        raw_suspend_task(task);
        list_insert_tail(&sem->wait_list, &task->list);
    }
    
    pthread_spin_unlock(&sem->wait_lock);

	return 0;
}

/*
 * sem_post - the function will try to get the semaphore and return the result
 *
 * @param sem - the point of the semaphore
 *
 * @return
 *           0 : success
 *     -EAGAIN : The semaphore was already locked, so it cannot be immediately locked by the
 *               sem_trywait() operation
 *    -EDEADLK : a deadlock condition was detected
 *      -EINTR : a signal interrupted this function
 *     -EINVAL : the sem argument does not refer to a valid semaphore
 */
int sem_trywait(sem_t *sem)
{
    int ret;
	void *task;
    
#if SEM_DEBUG_LEVEL     
	if (!sem)
		return -EINVAL;
#endif
    
    task = get_current();
	if (!task)
		return -EDEADLK;

	pthread_spin_lock(&sem->wait_lock);

    if (sem->count) {
        sem->count--;
        ret = 0;
    } else {
        ret = -EBUSY;
    }
    
	pthread_spin_unlock(&sem->wait_lock);

	return ret;
}

int sem_timedwait(sem_t *restrict sem,
                  const struct timespec *restrict abstime)
{
	return 0;
}

sem_t *sem_open(const char *name, int oflag, ...)
{
	return NULL;
}
